import java.util.*;

public class Explorer implements Runnable{
	static final int coes = 10;
	public final static double omg = 0.9,
						 del1 = 1.1,
						 del2 = 1.2;
    public final static int GTooMuch = 5, PTooMuch = 3;

	private double[] coe;
	private double[] dir;

	private double[] inm = new double[coes];

	private int pval;
	private double[] poe = new double[coes];
    private int pstop = 0;

	public int[] gval;
	public double[] goe;
	public boolean[] hasTested;

	private int[][][] pBottom = State.getpBottom();
	private int[][][] pTop = State.getpTop();
	private int[][] pWidth = State.getpWidth();
	private int[][] pHeight = State.getpHeight();

	private CallbackInterface callback;

	public Random r;

	private int ind;
	private int spawn;

	public Explorer(double[] coe, double[] dir, CallbackInterface callback, int ind){
		this.coe = coe;
		this.dir = dir;
		pval = 0;
		this.callback = callback;
		this.ind = ind;
	}

	@Override
	public void run(){
		callback.iAmDone(run(true), coe, ind, spawn);
	}

	public int run(boolean global){
		State s = new State();
		spawn = 0;
		while(!s.hasLost()){
			spawn ++;
			s.makeMove(pickMove(s,s.legalMoves()));
			s.drawNext(0,0);
		}
        pstop++;
		int val = s.getRowsCleared();
		if(val > pval){
			for(int i = 0; i < coes; i++){
				poe[i] = coe[i];
			}
			pval = val;
            pstop = 0;
		}
		
		return val;
	}

	public double[] getCoe(){
		return coe;
	}

	public double[] getDir(){
		return dir;
	}

	public void adjustDir(){
		// for(int i = 0; i < coes; i++){
		//	dir[i] = omg * dir[i] + r.nextDouble() * del1 * (goe[i] - coe[i]) + r.nextDouble() * del2 * (poe[i] - coe[i]);
		// }
	}

	public void move(){
        double r1 = r.nextDouble(), r2 = r.nextDouble();
        double pmul = 1, gmul = 1;
        if(gval[1] > GTooMuch){
            gmul = r.nextDouble();
        }
        if(pstop > PTooMuch){
            pmul = r.nextDouble();
        }
		for(int i = 0; i < coes; i++){
			// coe[i] = coe[i] + dir[i];
            coe[i] = omg * coe[i] + r1 * del1 * (goe[i] * gmul - coe[i]) + r2 * del2 * (poe[i] * pmul - coe[i]);
	
		}
	}


	private double h(int height, int changes, int touch, int holes, int rowsCleared, int block, int touchWall, double sd, int trnaistion, int colTransition){
		return  coe[0] * height + coe[2] * holes + coe[3] * touch + coe[4] * touchWall - coe[1] * rowsCleared
			+ coe[5] * block + coe[6] * changes + coe[7] * sd + coe[8] * trnaistion + coe[9] * colTransition;
	}

	private double utility(int[][] board, int[] top, int orient, int slot, int nextPiece){
		int height = top[slot]-pBottom[nextPiece][orient][0];
		int pw = pWidth[nextPiece][orient];
		int[] bottoms = pBottom[nextPiece][orient];
		int ph = pHeight[nextPiece][orient];
		int[] tops = pTop[nextPiece][orient];
		for(int c = 1; c < pw; c++) {
			height = Math.max(height,top[slot+c]-bottoms[c]);
		}
		if(height+ph >= State.ROWS)return Double.POSITIVE_INFINITY;
		int touchwall = 0;
		if(slot == 0){
			touchwall += tops[0] - bottoms[0];
		}else if(slot + pw == State.COLS){
			touchwall += tops[pw - 1] - bottoms[pw - 1];
		}
		for(int j = 0; j < pw; j++) {
			for(int h = height+bottoms[j]; h < height+tops[j]; h++) {
				board[h][j+slot] = -1;
			}
		}
		int rowsCleared = 0;
		int cleared = 0;
		for(int r = height+ph-1; r >= height; r--) {
			boolean full = true;
			for(int c = 0; c < State.COLS; c++) {
				if(board[r][c] == 0) {
					full = false;
					break;
				}
			}
			if(full) {
				rowsCleared++;
				cleared |= 1<<(height+ph-1 -r);
			}
		}

		int hh = 0, block = 0;
		int maxh = 0;
		int sd = 0, lastheight = 0, llheight = 0;
		for(int c = 0; c < top.length; c++){
			int h;
			if(c >= slot && c < pw){
				h = height + tops[c - slot];
			}else{
				h = top[c];
			}
			
		}
		int minh = 999, curh = 0;
		int transition = 0;
		int change = 0;
		int colTransition = 0;
		for(int j = 0; j < State.COLS; j++){
			int r = 0;
			int curhole = 0;
			boolean hasHole = false;
			int t = top[j];
			if(j >= slot && j < (slot + pw)){
				t = height + tops[j - slot];
			}
			curh = t;
			while(r <= t){
				if(board[r][j] == 0){
					if(!hasHole)colTransition++;
					hasHole = true;
					curhole++;
				}else{
					if(board[r][j] == -1 && (cleared & (1<<(height+ph-1 -r))) > 0){
						r++;
						curh--;
						continue;
					}
					if(hasHole){
						hh += curhole;
						block++;
						if(curhole != 0) colTransition++;
						curhole = 0;
					}
				}
				r++;
			}
			if(j > 1){
				if((lastheight < curh) && (lastheight < llheight)){
					sd += Math.min(curh - lastheight, llheight - lastheight);
				}
			}else if(j == 1 && lastheight < curh){
				sd += curh - lastheight;
			}
			if(j > 0){
				if(curh != lastheight){
					change++;
				}
			}
			llheight = lastheight;
			lastheight = curh;
			maxh = Math.max(curh, maxh);
			minh = Math.min(curh, minh);
		}
		for(int r = 0; r < (maxh + rowsCleared) && r < State.ROWS; r++){
			if(r >= height && r < (height + ph) && (cleared & (1<<(height+ph-1 -r))) > 0){
				continue;
			}
			for(int c = 0; c + 1 < State.COLS; c++){
				if((board[r][c] == 0 && board[r][c + 1] != 0) || (board[r][c] != 0 && board[r][c + 1] == 0)){
					transition++;
				}
			}
		}
		if(llheight > lastheight) sd += llheight - lastheight;
		int touch = 0;
		if(height == 0){
			for(int c = 0; c < pw; c++){
				if(bottoms[c] == 0)touch++;
			}
		}else{
			for(int c = 0; c < pw; c++){
				if(board[height - bottoms[c] + 1][slot + c] > 0)touch++;
			}
		}

		for(int j = 0; j < pw; j++) {
			for(int h = height+bottoms[j]; h < height+tops[j]; h++) {
				board[h][j+slot] = 0;
				if(j + slot + 1 < State.COLS && board[h][j + slot + 1] > 0)touch++;
				if(j + slot - 1 >= 0 && board[h][j + slot - 1] > 0)touch++;
			}
		}
		return h(height, change, -touch, hh, rowsCleared, block, -touchwall, sd, transition, colTransition);
	}

	private int pickMove(State s, int[][] legalMoves) {
		int[][] board = s.getField();
		int[] top = s.getTop();
		int best = 0;
		int nextPiece = s.getNextPiece();
		double bh = Double.MAX_VALUE;
		for(int i = 0; i < legalMoves.length; i++){
			double val = utility(board, top, legalMoves[i][0], legalMoves[i][1], nextPiece);
			if(val < bh){
				best = i;
				bh = val;
			}
		}

		return best;
	}
}
